package io.kiki.stack.netty.channelManager;

import io.kiki.stack.netty.command.Command;
import io.kiki.stack.netty.invoker.RequestContext;
import io.kiki.stack.netty.invoker.RpcCommandType;
import io.kiki.stack.netty.userProcessor.UserProcessor;
import io.kiki.stack.netty.util.StringUtils;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ConcurrentHashMap;


@Slf4j
@Setter
@Getter
public class ChannelContext implements Cloneable {

    @Setter
    @Getter
    private ChannelHandlerContext channelHandlerContext;
    private boolean serverSide = false;
    /**
     * whether need handle request timeout, if true, request will be discarded. The default value is true
     */
    private boolean timeoutDiscard = true;

    private long arriveTimestamp;

    private int timeout;

    private int rpcCommandType;
    private ConcurrentHashMap<String, UserProcessor<?>> userProcessors;
    private RequestContext requestContext;

    public ChannelContext(ChannelHandlerContext channelHandlerContext) {
        this.channelHandlerContext = channelHandlerContext;
    }

    public ChannelContext(ChannelHandlerContext channelHandlerContext, boolean serverSide) {
        this.channelHandlerContext = channelHandlerContext;
        this.serverSide = serverSide;
    }

    public ChannelContext(ChannelHandlerContext channelHandlerContext, boolean serverSide, ConcurrentHashMap<String, UserProcessor<?>> userProcessors) {
        this.channelHandlerContext = channelHandlerContext;
        this.serverSide = serverSide;
        this.userProcessors = userProcessors;
    }


    public ChannelContext(ChannelHandlerContext channelHandlerContext, RequestContext requestContext, boolean serverSide, ConcurrentHashMap<String, UserProcessor<?>> userProcessors) {
        this.channelHandlerContext = channelHandlerContext;
        this.serverSide = serverSide;
        this.userProcessors = userProcessors;
        this.requestContext = requestContext;
    }

    public ChannelFuture writeAndFlush(Command command) {
        return this.channelHandlerContext.writeAndFlush(command);
    }


    public ChannelFuture write(Command command) {
        return this.channelHandlerContext.write(command);
    }


    public void flush() {
        this.channelHandlerContext.flush();
    }


    public boolean isRequestTimeout() {
        return this.timeout > 0 && (this.rpcCommandType != RpcCommandType.REQUEST_ONEWAY) && (System.currentTimeMillis() - this.arriveTimestamp) > this.timeout;
    }


    public UserProcessor<?> getUserProcessor(String className) {
        return StringUtils.isBlank(className) ? null : this.userProcessors.get(className);
    }


    public Channel getChannel() {
        io.netty.channel.Channel channel = channelHandlerContext.channel();
        if (channel == null) {
            return null;
        }
        Attribute<Channel> attribute = channel.attr(Channel.CHANNEL);
        if (attribute != null) {
            return attribute.get();
        }
        return null;
    }

    public ChannelContext setTimeoutDiscard(boolean failFastEnabled) {
        this.timeoutDiscard = failFastEnabled;
        return this;
    }


    public ChannelContext clone() {//
        return new ChannelContext(channelHandlerContext, new RequestContext(), serverSide, userProcessors);
    }
}
